﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using SuchSofts.OpenImage.Dialogs;
using SuchSofts.OpenImage.Lib.Image;
using SuchSofts.OpenImage.Lib.Operation.WorkingStatus;
using System.Diagnostics;
using SuchSofts.OpenImage.Lib.Operation;
using System.IO;
using System.Drawing.Imaging;

namespace SuchSofts.OpenImage
{
    internal partial class FormMain : Form
    {
        private WorkingStatusManager currentStatus;

        private string currentPage;
        private string currentFolder;
        private string currentDownloadingImageAddress;
        private string currentFileName;
        private ImageFilter currentFilter;

        private Queue<string> imageQueue;
        private WebBrowser preparingBrowser;
        private WebClient downloadingClient;

        private static readonly string preparingInitStatus = "Please create a new download task.";
        private static readonly string downloadingInitStatus = "...";
        private static readonly string preparingStoppedStatus = "Click [Start] to download.";
        private static readonly string downloadingStoppedStatus = "...";
        private static readonly string preparingPausedStatus = "Click [Start] to continue downloading.";
        private static readonly string downloadingPausedStatus = "PAUSE";
        private static readonly string preparingDoingStatus = "Step 1: Generating image address list...";
        private static readonly string preparingDoneStatus = "Step 1: Generating image address list. [DONE]";
        private static readonly string downloadingDoingStatus = "Step 2: Downloading image {0}/{1}...";
        private static readonly string downloadingDoneStatus = "Step 2: Downloading {0} images. [DONE]";
        private static readonly string downloadingDoingToolTip = "Download from: {0}\nSave as: {1}";

        public FormMain()
        {
            InitializeComponent();

            currentPage = "http://";
            currentFolder = Properties.Settings.Default.DefaultFolder;
            currentFileName = "";
            currentDownloadingImageAddress = "";

            currentFilter = new ImageFilter();
            imageQueue = null;

            currentStatus = new WorkingStatusManager();
            currentStatus.StatusChanged += new WorkingStatusManager.StatusChangedEventHandler(currentStatus_StatusChanged);

            preparingBrowser = null;
            downloadingClient = null;

            toolStripMenuItemIsOpenPageWhenStart.Checked = Properties.Settings.Default.IsOpenPageWhenStart;
            toolStripMenuItemIsOpenFolderWhenComplete.Checked = Properties.Settings.Default.IsOpenFolderWhenComplete;
            toolStripMenuItemIsCloseWindowWhenComplete.Checked = Properties.Settings.Default.IsCloseWindowWhenComplete;
            toolStripMenuItemIsEmptyFolderWhenStart.Checked = Properties.Settings.Default.IsEmptyFolderWhenStart;
            toolStripMenuItemIsAutoRenameAsIndex.Checked = Properties.Settings.Default.IsAutoRenameAsIndex;
        }

        public FormMain(string[] args)
            : this()
        {
            try
            {
                currentPage = args[1].Trim();
                currentFolder = args[2].Trim();

                string type = args[3].Trim();
                Size min = new Size(Convert.ToInt32(args[4].Trim()), Convert.ToInt32(args[5].Trim()));
                Size max = new Size(Convert.ToInt32(args[6].Trim()), Convert.ToInt32(args[7].Trim()));
                currentFilter = new ImageFilter(type, min, max);

                currentStatus.Status = (WorkingStatus)Enum.Parse(typeof(WorkingStatus), args[0], true);
            }
            catch (Exception ex)
            {
                MessageBox.Show("An error occured while loading arguments.\nDetails: " + ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                Application.Exit();
            }
        }

        #region Preparing

        private void prepareFolderPathAndImageAddresses()
        {
            if (currentStatus.Status == WorkingStatus.Preparing)
            {
                try
                {
                    if (!Directory.Exists(currentFolder))
                    {
                        Directory.CreateDirectory(currentFolder);
                    }
                    if (Properties.Settings.Default.IsEmptyFolderWhenStart)
                    {
                        CommonOperations.EmptyFolder(currentFolder);
                    }
                    preparingBrowser = new WebBrowser();
                    preparingBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(preparingBrowser_DocumentCompleted);
                    preparingBrowser.NewWindow += delegate(object sender, CancelEventArgs e) { e.Cancel = true; };
                    preparingBrowser.Navigate(currentPage);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("An error occured while generating image list.\nDetails: " + ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    currentStatus.Status = WorkingStatus.Stopped;
                }
            }
        }

        private void preparingBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            if (currentStatus.Status == WorkingStatus.Preparing)
            {
                try
                {
                    if (e.Url == preparingBrowser.Document.Url)
                    {
                        preparingBrowser.Stop();
                        imageQueue = new Queue<string>();
                        string[] allowedTypes = currentFilter.Types.Split('|');
                        bool isAllowAllTypes = false;
                        foreach (string type in allowedTypes)
                        {
                            if (type.Equals(ImageFilter.AllTypes[ImageFilter.AllTypes.Count - 1], StringComparison.OrdinalIgnoreCase))
                            {
                                isAllowAllTypes = true;
                            }
                        }
                        foreach (HtmlElement imageElement in preparingBrowser.Document.Images)
                        {
                            string src = imageElement.GetAttribute("src");

                            if (src == "")
                            {
                                continue;
                            }

                            bool isAllowType = false;
                            if (isAllowAllTypes)
                            {
                                isAllowType = true;
                            }
                            else
                            {
                                foreach (string type in allowedTypes)
                                {
                                    if (src.EndsWith("." + type, StringComparison.OrdinalIgnoreCase))
                                    {
                                        isAllowType = true;
                                        break;
                                    }
                                }
                            }
                            bool isAdded = false;
                            if (isAllowType)
                            {
                                foreach (string address in imageQueue)
                                {
                                    if (address.Equals(src, StringComparison.OrdinalIgnoreCase))
                                    {
                                        isAdded = true;
                                    }
                                }
                            }
                            if (isAllowType && (!isAdded))
                            {
                                imageQueue.Enqueue(src);
                            }
                        }
                        preparingBrowser.Dispose();
                        currentStatus.Status = WorkingStatus.Downloading;
                    }
                }

                catch (Exception ex)
                {
                    MessageBox.Show("An error occured while generating image list.\nDetails: " + ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    currentStatus.Status = WorkingStatus.Stopped;
                }
            }
        }

        #endregion

        #region Downloading

        private bool downloadNext()
        {
            if (currentStatus.Status == WorkingStatus.Downloading)
            {
                try
                {
                    if (imageQueue.Count > 0)
                    {
                        labelDownloadingStatus.Text = String.Format(downloadingDoingStatus, progressBarDownloadingProgress.Value + 1, progressBarDownloadingProgress.Maximum);
                        if (downloadingClient != null)
                        {
                            downloadingClient.Dispose();
                        }
                        downloadingClient = new WebClient();
                        downloadingClient.DownloadFileCompleted += new AsyncCompletedEventHandler(downloadingClient_DownloadFileCompleted);
                        currentDownloadingImageAddress = imageQueue.Dequeue();
                        currentFileName = CommonOperations.ConvertToLegalFileName(currentDownloadingImageAddress.Substring(currentDownloadingImageAddress.LastIndexOf("/") + 1));

                        if (Properties.Settings.Default.IsAutoRenameAsIndex)
                        {
                            string exName = "";
                            int exStartIndex = currentFileName.LastIndexOf(".");
                            if (exStartIndex != -1)
                            {
                                exName = currentFileName.Substring(exStartIndex);
                            }
                            string[] allowedTypes = currentFilter.Types.Split('|');
                            foreach (string type in allowedTypes)
                            {
                                if (!exName.Equals(type, StringComparison.OrdinalIgnoreCase))
                                {
                                    exName = "";
                                }
                            }
                            currentFileName = (progressBarDownloadingProgress.Value + 1).ToString() + exName;
                        }

                        CommonOperations.IsFileExisted(currentFolder, ref currentFileName);
                        string filePath = currentFolder + currentFileName;
                        toolTipWorkingDetails.SetToolTip(labelDownloadingStatus, string.Format(downloadingDoingToolTip, currentDownloadingImageAddress, filePath));
                        downloadingClient.DownloadFileAsync(new Uri(currentDownloadingImageAddress), filePath);
                        return true;
                    }
                    else
                    {
                        if (downloadingClient != null)
                        {
                            downloadingClient.Dispose();
                        }
                        return false;
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("An error occured while downloading images.\nDetails: " + ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    currentStatus.Status = WorkingStatus.Stopped;
                    return false;
                }
            }
            else
            {
                return false;
            }
        }

        private void downloadingClient_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
        {
            if (!e.Cancelled)
            {
                if (currentStatus.Status == WorkingStatus.Downloading)
                {
                    try
                    {
                        //Filter
                        string imagePath = currentFolder + currentFileName;

                        bool canAddProgressValue = true;
                        if (new FileInfo(imagePath).Length == 0)
                        {
                            canAddProgressValue = false;
                        }
                        if (canAddProgressValue)
                        {
                            try
                            {
                                Image imageTest = Image.FromFile(imagePath);
                                if (imageTest.Width < currentFilter.MinimumSize.Width
                                    || imageTest.Height < currentFilter.MinimumSize.Height
                                    || imageTest.Width > currentFilter.MaximumSize.Width
                                    || imageTest.Height > currentFilter.MaximumSize.Height)
                                {
                                    canAddProgressValue = false;
                                }
                                else
                                {
                                    string dotExName = "." + CommonOperations.GetImageExtension(imageTest);
                                    if (!imagePath.EndsWith(dotExName, StringComparison.OrdinalIgnoreCase))
                                    {
                                        if (!(imagePath.ToLower().EndsWith(".jpg") && dotExName.ToLower() == ".jpeg"))
                                        {
                                            try
                                            {
                                                imageTest.Dispose();
                                                File.Move(imagePath, imagePath + dotExName);
                                            }
                                            catch (Exception ex)
                                            {
                                                Debug.Fail(ex.Message, ex.StackTrace);
                                            }
                                        }
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                Debug.Fail(ex.Message, ex.StackTrace);
                                canAddProgressValue = false;
                            }
                        }

                        if (canAddProgressValue)
                        {
                            ++progressBarDownloadingProgress.Value;
                        }
                        else
                        {
                            try
                            {
                                File.Delete(imagePath);
                            }
                            catch (Exception ex)
                            {
                                Debug.Fail(ex.Message, ex.StackTrace);
                            }
                            --progressBarDownloadingProgress.Maximum;
                        }

                        if (!downloadNext())
                        {
                            currentStatus.Status = WorkingStatus.Finished;
                            return;
                        }
                    }
                    catch (Exception ex)
                    {
                        Debug.Fail(currentFolder + currentFileName);
                        MessageBox.Show("An error occured while downloading images.\nDetails: " + ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                        currentStatus.Status = WorkingStatus.Stopped;
                    }
                }
            }
            else
            {
                try
                {
                    File.Delete(currentFolder + currentFileName);
                }
                catch (Exception ex)
                {
                    Debug.Fail(ex.Message, ex.StackTrace);
                }
            }
        }

        #endregion

        #region Downloading
        #endregion

        private void currentStatus_StatusChanged(object sender, StatusChangedEventArgs e)
        {
            try
            {
                toolStripTextBoxStatus.Text = e.NewStatus.ToString();
                if (e.NewStatus != WorkingStatus.Downloading)
                {
                    toolTipWorkingDetails.SetToolTip(labelDownloadingStatus, "");
                }
                switch (e.NewStatus)
                {
                    case WorkingStatus.Ready:
                        buttonStart.Enabled
                            = buttonPause.Enabled
                            = buttonStop.Enabled
                            = buttonOpenFolder.Enabled
                            = toolStripMenuItemStart.Enabled
                            = toolStripMenuItemPause.Enabled
                            = toolStripMenuItemStop.Enabled
                            = toolStripMenuItemOpenWebPage.Enabled
                            = toolStripMenuItemOpenImageFolder.Enabled
                            = false;

                        labelPreparingStatus.Text = preparingInitStatus;
                        labelDownloadingStatus.Text = downloadingInitStatus;

                        progressBarPreparingProgress.Style = ProgressBarStyle.Blocks;
                        progressBarPreparingProgress.Maximum = 1;
                        progressBarPreparingProgress.Value = 0;
                        progressBarDownloadingProgress.Maximum = 1;
                        progressBarDownloadingProgress.Value = 0;
                        break;

                    case WorkingStatus.Preparing:
                        if (e.OldStatus == WorkingStatus.Finished)
                        {
                            if (MessageBox.Show("Do you want to download again?", Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
                            {
                                currentStatus.Status = WorkingStatus.Finished;
                                return;
                            }
                        }

                        buttonStart.Enabled
                            = buttonPause.Enabled
                            = toolStripMenuItemStart.Enabled
                            = toolStripMenuItemPause.Enabled
                            = false;
                        buttonStop.Enabled
                            = buttonOpenFolder.Enabled
                            = toolStripMenuItemStop.Enabled
                            = toolStripMenuItemOpenWebPage.Enabled
                            = toolStripMenuItemOpenImageFolder.Enabled
                            = true;

                        labelPreparingStatus.Text = preparingDoingStatus;
                        labelDownloadingStatus.Text = downloadingInitStatus;

                        progressBarPreparingProgress.Style = ProgressBarStyle.Marquee;
                        progressBarPreparingProgress.Maximum = 1;
                        progressBarPreparingProgress.Value = 0;
                        progressBarDownloadingProgress.Maximum = 1;
                        progressBarDownloadingProgress.Value = 0;

                        if (Properties.Settings.Default.IsOpenPageWhenStart)
                        {
                            CommonOperations.Navigate(currentPage);
                        }

                        prepareFolderPathAndImageAddresses();
                        break;

                    case WorkingStatus.Downloading:
                        buttonStart.Enabled
                            = toolStripMenuItemStart.Enabled
                            = false;
                        buttonPause.Enabled
                            = buttonStop.Enabled
                            = buttonOpenFolder.Enabled
                            = toolStripMenuItemPause.Enabled
                            = toolStripMenuItemStop.Enabled
                            = toolStripMenuItemOpenWebPage.Enabled
                            = toolStripMenuItemOpenImageFolder.Enabled
                            = true;


                        if (e.OldStatus == WorkingStatus.Paused)
                        {
                            labelPreparingStatus.Text = preparingDoneStatus;
                            labelDownloadingStatus.Text = string.Format(downloadingDoingStatus, progressBarDownloadingProgress.Maximum - imageQueue.Count, progressBarDownloadingProgress.Maximum);

                            progressBarDownloadingProgress.Value = progressBarDownloadingProgress.Maximum - imageQueue.Count;
                        }
                        else
                        {
                            labelPreparingStatus.Text = preparingDoneStatus;
                            labelDownloadingStatus.Text = String.Format(downloadingDoingStatus, progressBarDownloadingProgress.Value + 1, progressBarDownloadingProgress.Maximum);

                            progressBarPreparingProgress.Style = ProgressBarStyle.Blocks;
                            progressBarPreparingProgress.Maximum = 1;
                            progressBarPreparingProgress.Value = 1;
                            progressBarDownloadingProgress.Maximum = imageQueue.Count;
                        }

                        if (!downloadNext())
                        {
                            currentStatus.Status = WorkingStatus.Finished;
                        }
                        break;

                    case WorkingStatus.Paused:
                        buttonPause.Enabled
                            = toolStripMenuItemPause.Enabled
                            = false;
                        buttonStart.Enabled
                            = buttonStop.Enabled
                            = buttonOpenFolder.Enabled
                            = toolStripMenuItemStart.Enabled
                            = toolStripMenuItemStop.Enabled
                            = toolStripMenuItemOpenWebPage.Enabled
                            = toolStripMenuItemOpenImageFolder.Enabled
                            = true;

                        labelPreparingStatus.Text = preparingPausedStatus;
                        labelDownloadingStatus.Text = downloadingPausedStatus;
                        downloadingClient.CancelAsync();
                        imageQueue.Enqueue(currentDownloadingImageAddress);
                        break;
                    case WorkingStatus.Stopped:
                        buttonStop.Enabled
                            = toolStripMenuItemStop.Enabled
                            = buttonPause.Enabled
                            = toolStripMenuItemPause.Enabled
                            = false;
                        buttonStart.Enabled
                            = buttonOpenFolder.Enabled
                            = toolStripMenuItemStart.Enabled
                            = toolStripMenuItemOpenWebPage.Enabled
                            = toolStripMenuItemOpenImageFolder.Enabled
                            = true;

                        if (preparingBrowser != null)
                        {
                            preparingBrowser.Stop();
                            preparingBrowser.Dispose();
                        }
                        if (downloadingClient != null)
                        {
                            downloadingClient.CancelAsync();
                            downloadingClient.Dispose();
                        }

                        labelPreparingStatus.Text = preparingStoppedStatus;
                        labelDownloadingStatus.Text = downloadingStoppedStatus;

                        progressBarPreparingProgress.Style = ProgressBarStyle.Blocks;
                        progressBarPreparingProgress.Maximum = 1;
                        progressBarPreparingProgress.Value = 0;
                        progressBarDownloadingProgress.Maximum = 1;
                        progressBarDownloadingProgress.Value = 0;
                        break;

                    case WorkingStatus.Finished:
                        buttonPause.Enabled
                            = buttonStop.Enabled
                            = toolStripMenuItemPause.Enabled
                            = toolStripMenuItemStop.Enabled
                            = false;
                        buttonStart.Enabled
                            = buttonOpenFolder.Enabled
                            = toolStripMenuItemStart.Enabled
                            = toolStripMenuItemOpenWebPage.Enabled
                            = toolStripMenuItemOpenImageFolder.Enabled
                            = true;

                        labelPreparingStatus.Text = preparingDoneStatus;
                        labelDownloadingStatus.Text = string.Format(downloadingDoneStatus, progressBarDownloadingProgress.Maximum);

                        progressBarPreparingProgress.Value = progressBarPreparingProgress.Maximum = 1;
                        progressBarDownloadingProgress.Value = progressBarDownloadingProgress.Maximum = 1;

                        if (e.OldStatus == WorkingStatus.Downloading)
                        {
                            if (Properties.Settings.Default.IsOpenFolderWhenComplete)
                            {
                                CommonOperations.Browse(currentFolder);
                            }
                            if (Properties.Settings.Default.IsCloseWindowWhenComplete)
                            {
                                Application.Exit();
                            }
                        }
                        break;
                }
            }
            catch (Exception ex)
            {
                Debug.Fail(ex.Message, ex.StackTrace);
                currentStatus.Status = WorkingStatus.Stopped;
            }
        }

        private void toolStripMenuItemAbout_Click(object sender, EventArgs e)
        {
            new FormAbout().ShowDialog();
        }

        #region Save Settings

        private void toolStripMenuItemIsOpenPageWhenStart_CheckedChanged(object sender, EventArgs e)
        {
            Properties.Settings.Default.IsOpenPageWhenStart = toolStripMenuItemIsOpenPageWhenStart.Checked;
        }

        private void toolStripMenuItemIsOpenFolderWhenComplete_CheckedChanged(object sender, EventArgs e)
        {
            Properties.Settings.Default.IsOpenFolderWhenComplete = toolStripMenuItemIsOpenFolderWhenComplete.Checked;
        }

        private void toolStripMenuItemIsCloseWindowWhenComplete_CheckedChanged(object sender, EventArgs e)
        {
            Properties.Settings.Default.IsCloseWindowWhenComplete = toolStripMenuItemIsCloseWindowWhenComplete.Checked;
        }

        private void FormMain_FormClosed(object sender, FormClosedEventArgs e)
        {
            Properties.Settings.Default.Save();
        }

        #endregion

        private void createTask(string page, string folder, ImageFilter filter)
        {
            FormNew formNew = new FormNew(page, folder, filter);
            formNew.ShowDialog();
            if (formNew.IsFinished)
            {
                currentPage = formNew.Page;
                currentFolder = formNew.Folder;
                currentFilter = formNew.Filter;
                if (currentStatus.Status != WorkingStatus.Preparing && currentStatus.Status != WorkingStatus.Downloading)
                {
                    currentStatus.Status = WorkingStatus.Ready;
                    if (formNew.IsDownloadNow)
                    {
                        currentStatus.Status = WorkingStatus.Preparing;
                    }
                    else
                    {
                        currentStatus.Status = WorkingStatus.Stopped;
                    }
                }
                else
                {
                    try
                    {
                        StringBuilder argsBuilder = new StringBuilder();
                        argsBuilder.Append("\"" + (formNew.IsDownloadNow ? WorkingStatus.Preparing.ToString() : WorkingStatus.Stopped.ToString()) + " \" ");
                        argsBuilder.Append("\"" + formNew.Page + " \" ");
                        argsBuilder.Append("\"" + formNew.Folder + " \" ");
                        argsBuilder.Append("\"" + formNew.Filter.Types + " \" ");
                        argsBuilder.Append("\"" + formNew.Filter.MinimumSize.Width.ToString() + " \" ");
                        argsBuilder.Append("\"" + formNew.Filter.MinimumSize.Height.ToString() + " \" ");
                        argsBuilder.Append("\"" + formNew.Filter.MaximumSize.Width.ToString() + " \" ");
                        argsBuilder.Append("\"" + formNew.Filter.MaximumSize.Height.ToString() + " \"");
                        Process.Start(Application.ExecutablePath, argsBuilder.ToString());
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show("An error occured while starting a new instance of " + Application.ProductName + ".\nDetails: " + ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
            }
        }

        private void toolStripMenuItemNewTask_Click(object sender, EventArgs e)
        {
            createTask(currentPage, currentFolder, currentFilter);
        }

        private void toolStripMenuItemExit_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void buttonStart_Click(object sender, EventArgs e)
        {
            if (currentStatus.Status == WorkingStatus.Paused)
            {
                currentStatus.Status = WorkingStatus.Downloading;
            }
            else
            {
                currentStatus.Status = WorkingStatus.Preparing;
            }
        }

        private void buttonStop_Click(object sender, EventArgs e)
        {
            currentStatus.Status = WorkingStatus.Stopped;
        }

        private void buttonPause_Click(object sender, EventArgs e)
        {
            currentStatus.Status = WorkingStatus.Paused;
        }

        private void buttonOpenFolder_Click(object sender, EventArgs e)
        {
            CommonOperations.Browse(currentFolder);
        }

        private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (currentStatus.Status == WorkingStatus.Preparing || currentStatus.Status == WorkingStatus.Downloading)
            {
                if (MessageBox.Show(Application.ProductName + " is still working.\nDo you really want to exit?", Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
                {
                    e.Cancel = true;
                }
            }
        }

        private void toolStripMenuItemOpenWebPage_Click(object sender, EventArgs e)
        {
            CommonOperations.Navigate(currentPage);
        }

        private void toolStripMenuItemIsEmptyFolderWhenStart_CheckedChanged(object sender, EventArgs e)
        {
            Properties.Settings.Default.IsEmptyFolderWhenStart = toolStripMenuItemIsEmptyFolderWhenStart.Checked;
        }

        private void toolStripMenuItemIsAutoRenameAsIndex_CheckedChanged(object sender, EventArgs e)
        {
            Properties.Settings.Default.IsAutoRenameAsIndex = toolStripMenuItemIsAutoRenameAsIndex.Checked;
        }

    }
}
